package com.eureka.provider.service.impl;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.eureka.provider.lock.ServiceLock;
import com.eureka.provider.mapper.GoodsMapper;
import com.eureka.provider.mapper.SeckillGoodsMapper;
import com.eureka.provider.mapper.SeckillOrderMapper;
import com.eureka.provider.pojo.*;
import com.eureka.provider.queue.jvm.SeckillQueue;
import com.eureka.provider.service.IGoodsService;
import com.eureka.provider.service.IOrderService;
import com.eureka.provider.service.ISeckillGoodsService;
import com.eureka.provider.service.ISeckillOrderService;
import com.eureka.provider.vo.GoodsVo;
import com.eureka.provider.vo.RespBean;
import com.eureka.provider.vo.RespBeanEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author sam
 * @since 2021-04-15
 */
@Service
@Slf4j
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements IGoodsService {
    @Autowired
    GoodsMapper goodsMapper;
    @Autowired
    ISeckillOrderService seckillOrderService;
    @Autowired
    SeckillOrderMapper seckillOrderMapper;
    @Autowired
    ISeckillGoodsService seckillGoodsService;
    @Autowired
    IOrderService orderService;
    @Autowired
    RedisTemplate redisTemplate;
    @Override
    public List<GoodsVo> findGoodsVo() {
        List<GoodsVo> goodsVo = goodsMapper.findGoodsVo();
        return goodsVo;
    }

    @Override
    public GoodsVo findGoodsVoById(Long id) {
        GoodsVo goods=goodsMapper.findGoodsVoById(id);
        Date start=goods.getStartDate();
        Date end=goods.getEndDate();
        Date now=new Date();
        int remainTime=0;
        if(start.before(now) && now.before(end)){
            goods.setState(2);
        }else if(end.before(now) || goods.getSeckillStock()<=0){
            goods.setState(3);
            remainTime= (int) ((now.getTime()-end.getTime())/1000);
        }else{
            remainTime= (int) ((start.getTime()-now.getTime())/1000);
            goods.setState(1);
        }
        goods.setRemainTime(remainTime);
        return goods;
    }

    @Override
    public GoodsVo findGoodsVoByIdRedis(Long id) {
        ValueOperations valueOperations = redisTemplate.opsForValue();
        GoodsVo goods =(GoodsVo) valueOperations.get("goods:" + id);
        //缓存未命中
        if(goods==null){
            //log.info("更新goods:"+id);
            goods=goodsMapper.findGoodsVoById(id);
        }
        valueOperations.set("goods:"+id,goods,20, TimeUnit.SECONDS);
        //valueOperations.set("goods:"+id,goods);
        Date start=goods.getStartDate();
        Date end=goods.getEndDate();
        Date now=new Date();
        int remainTime=0;
        if(start.before(now) && now.before(end)){
            goods.setState(2);
        }else if(end.before(now) || goods.getSeckillStock()<=0){
            goods.setState(3);
            remainTime= (int) ((now.getTime()-end.getTime())/1000);
        }else{
            remainTime= (int) ((start.getTime()-now.getTime())/1000);
            goods.setState(1);
        }
        goods.setRemainTime(remainTime);
        return goods;
    }

    public boolean deleteGoodsVoByIdRedis(Long id){
        Boolean delete = redisTemplate.delete("goods:" + id);
        return delete;
    }
    @Override
    public RespBean doSecKill5(User user, Long goodsId) {
        GoodsVo goods = findGoodsVoByIdRedis(goodsId);
        if(goods.getSeckillStock()<1){
            return RespBean.Error(RespBeanEnum.EMPTY_STOCK);
        }
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:"+user.getId()+goodsId);
        if(seckillOrder!=null){
            return RespBean.Error(RespBeanEnum.MUTIL_ORDER);
        }
        goods.setSeckillStock(goods.getSeckillStock()-1);
        redisTemplate.opsForValue().set("goods:" + goodsId,goods);
        Order order=new Order();
        order.setUserId(user.getId());
        order.setGoodsId(goods.getId());
        order.setGoodsCount(1);
        order.setGoodsPrice(goods.getGoodsPrice());
        order.setCreatDate(new Date());
        order.setStatus(0);
        SeckillQueue.getSkillQueue().produce(order);
        return RespBean.Success(order);
    }

    @Override
    public RespBean doSecKill6(User user, Long goodsId) {
        GoodsVo goods = findGoodsVoByIdRedis(goodsId);
        if(goods.getSeckillStock()<1){
            return RespBean.Error(RespBeanEnum.EMPTY_STOCK);
        }
        SeckillOrder seckillOrder = (SeckillOrder) redisTemplate.opsForValue().get("order:"+user.getId()+goodsId);
        if(seckillOrder!=null){
            return RespBean.Error(RespBeanEnum.MUTIL_ORDER);
        }
        SeckillGoods updateGoods=seckillGoodsMapper.selectById(goodsId);
        updateGoods.setSeckillStock(updateGoods.getSeckillStock()-1);
        int i = seckillGoodsMapper.updateById(updateGoods);
        if(i==1){
            //缓存淘汰策略
//            redisTemplate.delete("goods:"+goodsId);
            Order order=orderService.secKill(user,goods);
            log.info(user.getId()+RespBeanEnum.SUCCESS.getMsg());
            return RespBean.Success(order);
        }else{
            return RespBean.Error(RespBeanEnum.EMPTY_STOCK);
        }
    }

    /**
     * 阻塞队列
     * @param user
     * @param goodsId
     * @return
     */
    @Override
    @Transactional
    public RespBean doSecKill4(User user, Long goodsId) {
        GoodsVo goods = findGoodsVoById(goodsId);
        //判断库存,可能出现超卖
//        if(goods.getSeckillStock()<1){
//            //log.info(user.getId()+ RespBeanEnum.EMPTY_STOCK.getMsg());
//            return RespBean.Error(RespBeanEnum.EMPTY_STOCK);
//        }
//        //判断是否重复抢购
//        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>()
//                .eq("user_id", user.getId())
//                .eq("goods_id", goodsId));
//        if(seckillOrder!=null){
//            log.info(user.getId()+RespBeanEnum.MUTIL_ORDER.getMsg());
//            return RespBean.Error(RespBeanEnum.MUTIL_ORDER);
//        }
        Order order=new Order();
        order.setUserId(user.getId());
        order.setGoodsId(goods.getId());
        order.setGoodsCount(1);
        order.setGoodsPrice(goods.getGoodsPrice());
        order.setCreatDate(new Date());
        order.setStatus(0);
        SeckillQueue.getSkillQueue().produce(order);
        return null;
    }
    /**
     * 数据库乐观锁
     */
    @Autowired
    SeckillGoodsMapper seckillGoodsMapper;
    @Override
    public RespBean doSecKill3(User user, Long goodsId) {
        //GoodsVo goods = findGoodsVoById(goodsId);
        SeckillGoods goods=seckillGoodsMapper.selectById(goodsId);
        //判断库存,可能出现超卖
        if(goods.getSeckillStock()<1){
            log.info(user.getId()+ RespBeanEnum.EMPTY_STOCK.getMsg());
            return RespBean.Error(RespBeanEnum.EMPTY_STOCK);
        }
        //判断是否重复抢购
        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>()
                .eq("user_id", user.getId())
                .eq("goods_id", goodsId));
        if(seckillOrder!=null){
            log.info(user.getId()+RespBeanEnum.MUTIL_ORDER.getMsg());
            return RespBean.Error(RespBeanEnum.MUTIL_ORDER);
        }
        /**
         *更新库存
         */
        goods.setSeckillStock(goods.getSeckillStock()-1);
        int i = seckillGoodsMapper.updateById(goods);
        if(i==1){
            GoodsVo goodsvo = findGoodsVoById(goodsId);
            Order order=orderService.secKill(user,goodsvo);
            log.info(user.getId()+RespBeanEnum.SUCCESS.getMsg());
            return RespBean.Success(order);
        }else{
            return RespBean.Error(RespBeanEnum.EMPTY_STOCK);
        }
    }

    /**
     * 数据库悲观锁 QPS 47
     * @param user
     * @param goodsId
     * @return
     */
    @Override
    @Transactional
    @SentinelResource(value = "kill",blockHandler = "handler")
    public RespBean doSecKill2(User user, Long goodsId) {
        GoodsVo goods = findGoodsVoById(goodsId);
        //判断库存,可能出现超卖
        if(goods.getSeckillStock()<1){
            log.info(user.getId()+ RespBeanEnum.EMPTY_STOCK.getMsg());
            return RespBean.Error(RespBeanEnum.EMPTY_STOCK);
        }
        //判断是否重复抢购
        SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>()
                .eq("user_id", user.getId())
                .eq("goods_id", goodsId));
        if(seckillOrder!=null){
            log.info(user.getId()+RespBeanEnum.MUTIL_ORDER.getMsg());
            return RespBean.Error(RespBeanEnum.MUTIL_ORDER);
        }
        /**
         *更新库存
         */
        boolean update = seckillGoodsService.update(new UpdateWrapper<SeckillGoods>()
                .eq("id", goods.getId())
                .gt("seckill_stock", 0)
                .setSql("seckill_stock=seckill_stock-1"));
        //创建订单
        if(update){
            Order order=orderService.secKill(user,goods);
            log.info(user.getId()+RespBeanEnum.SUCCESS.getMsg());
            return RespBean.Success(order);
        }else{
            //log.info(user.getId()+ RespBeanEnum.EMPTY_STOCK.getMsg());
            return RespBean.Error(RespBeanEnum.EMPTY_STOCK);
        }
    }
    public RespBean handler(User user, Long goodsId, BlockException ex){
        log.info("被限流了");
        return RespBean.Error();
    }
    /**
     * 程序锁
     * QPS 13
     * @param user
     * @param goodsId
     * @return
     */
    ReentrantLock lock=new ReentrantLock();
    @Override
    //@Transactional
    /**
     * 原来lock锁是在事物单元中执行的,秒杀开始后，某个事物在未提交之前，锁已经释放(事物提交是在整个方法执行完)，导致下一个事物读取到了上个事物未提交的数据，也就是传说中的脏读。此处给出的建议是锁上移，也就是说要包住整个事物单元。
     */
    @ServiceLock("doSecKillSync")
    public RespBean doSecKillSync(User user, Long goodsId) throws Exception {

            GoodsVo goods = findGoodsVoById(goodsId);
            //判断库存,可能出现超卖
            if(goods.getSeckillStock()<1){
                //log.info(user.getId()+ RespBeanEnum.EMPTY_STOCK.getMsg());
                return RespBean.Error(RespBeanEnum.EMPTY_STOCK);
            }
            //判断是否重复抢购
            SeckillOrder seckillOrder = seckillOrderService.getOne(new QueryWrapper<SeckillOrder>()
                    .eq("user_id", user.getId())
                    .eq("goods_id", goodsId));
            if(seckillOrder!=null){
                log.info(user.getId()+RespBeanEnum.MUTIL_ORDER.getMsg());
                return RespBean.Error(RespBeanEnum.MUTIL_ORDER);
            }
            //更新库存
            SeckillGoods seckillGoods = seckillGoodsService.getOne(new QueryWrapper<SeckillGoods>()
                    .eq("goods_id", goods.getId()));
            seckillGoods.setSeckillStock(seckillGoods.getSeckillStock()-1);
            seckillGoodsService.updateById(seckillGoods);
            //创建订单
            Order order=orderService.secKill(user,goods);
            return RespBean.Success(order);
    }


}
